/**
 * 
 */
package io.gitee.falllee.dao.core.table;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;

import io.gitee.falllee.dao.config.DaoConfigContextHolder;
import io.gitee.falllee.dao.core.HeaderBuilder;
import io.gitee.falllee.dao.model.DaoConst;

/**
 * @author batty
 * @date 2022-11-28
 *
 */
public class ExtendBuilder extends HeaderBuilder {

    private TableBuilder dao;

    private String keyField;

    private String[] uniqueKey;
    
    private Boolean allowUpdateNullData;
    
    private String updateFields;
    
    ExtendBuilder(TableBuilder parent) {
        super(parent.getDaoConfig());
        this.dao = parent;
        this.keyField = parent.getKeyField();
    }

    /**
     * 增加唯一键（非主键）
     * 
     * @param uniqueKey
     * @return
     */
    public ExtendBuilder uniqueKey(String... uniqueKey) {
        this.uniqueKey = uniqueKey;
        return this;
    }

    /**
     * 是否允许更新空值，默认不更新
     * 
     * @return
     */
    public ExtendBuilder allowUpdateNullData() {
        this.allowUpdateNullData = true;
        return this;
    }

    /**
     * 更新字段
     * 
     * @param updateFields
     * @return
     */
    public ExtendBuilder updateFields(String updateFields) {
        this.updateFields = updateFields;
        return this;
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean saveData(Map<String, Object> t) {
        Object keyValue = restoreData(t);
        if (ObjectUtils.isEmpty(keyValue)) {
            return dao.insertBuilder().insert(t);
        } else {
            if (StringUtils.hasLength(updateFields)) {
                return dao.updateBuilder().updateFields(updateFields).update(t);
            }
            return Boolean.TRUE.equals(allowUpdateNullData) ? dao.updateBuilder().allowUpdateNullData().update(t)
                    : dao.updateBuilder().update(t);
        }
    }

    private boolean isEmpty(Map<String, Object> t, List<String> keyValueList) {
        Object keyValue = t.get(keyField);
        return ObjectUtils.isEmpty(keyValue) || DaoConst.STRING_ZERO.equals(keyValue.toString())
                || !keyValueList.contains(keyValue.toString());
    }

    private boolean isNotEmpty(Map<String, Object> t, List<String> keyValueList) {
        return !isEmpty(t, keyValueList);
    }

    @Transactional(rollbackFor = Exception.class)
    public Object restoreData(Map<String, Object> t) {
        List<Object> keyValueList = restoreDataList(Collections.singletonList(t));
        return CollectionUtils.isEmpty(keyValueList) ? null : keyValueList.get(0);
    }

    @Transactional(rollbackFor = Exception.class)
    public Boolean saveDataList(List<Map<String, Object>> list) {
        Boolean bl = true;
        List<Object> keyValueList = restoreDataList(list);
        List<String> keyValueStrList = keyValueList.stream().map(Object::toString).collect(Collectors.toList());
        List<Map<String, Object>> insertList = list.stream().filter(p -> isEmpty(p, keyValueStrList))
                .collect(Collectors.toList());
        List<Map<String, Object>> updateList = list.stream().filter(p -> isNotEmpty(p, keyValueStrList))
                .collect(Collectors.toList());
        if (!CollectionUtils.isEmpty(insertList)) {
            bl = dao.insertBuilder().insertList(insertList);
        }
        if (Boolean.TRUE.equals(bl) && !CollectionUtils.isEmpty(updateList)) {
            if (StringUtils.hasLength(updateFields)) {
                return dao.updateBuilder().updateFields(updateFields).updateBatch(updateList);
            }
            return Boolean.TRUE.equals(allowUpdateNullData)
                    ? dao.updateBuilder().allowUpdateNullData().updateBatch(updateList)
                    : dao.updateBuilder().updateBatch(updateList);
        }
        return bl;
    }

    @Transactional(rollbackFor = Exception.class)
    public List<Object> restoreDataList(List<Map<String, Object>> list) {
        boolean bl = DaoConfigContextHolder.getIncludeDisable();
        if (!bl) {
            DaoConfigContextHolder.setIncludeDisable(true);
        }
        try {
            // 如果存在唯一键，先按唯一键查询
            if (!ObjectUtils.isEmpty(uniqueKey)) {
                setFixedFieldValue(list);
                setCreateAndUpdateUser(list);
                List<Map<String, Object>> dbList = listDataByUniqueKey(list);
                List<Object> deleteKeyValueList = new ArrayList<>();
                for (Map<String, Object> db : dbList) {
                    Map<String, Object> uniqueKeyValueMap = new HashMap<>(16);
                    for (String key : uniqueKey) {
                        Object obj = db.get(key);
                        uniqueKeyValueMap.put(key, obj);
                    }
                    List<Map<String, Object>> subList = list.stream().filter(p -> {
                        for (String key : uniqueKey) {
                            Object obj = p.get(key);
                            if (!uniqueKeyValueMap.get(key).toString().equals(obj.toString())) {
                                return false;
                            }
                        }
                        return true;
                    }).collect(Collectors.toList());
                    for (Map<String, Object> t : subList) {
                        Object keyValue = t.get(keyField);
                        Object dbKeyValue = db.get(keyField);
                        // 如果主键为空，则回写主键，如果主键和数据库主键不一致，则将数据库硬删除
                        if (keyValue == null) {
                            t.put(keyField, dbKeyValue);
                        } else if (!keyValue.toString().equals(dbKeyValue.toString())
                                && !deleteKeyValueList.contains(dbKeyValue)) {
                            deleteKeyValueList.add(dbKeyValue);
                        }
                    }
                }
                // 需要硬删除
                if (!CollectionUtils.isEmpty(deleteKeyValueList)) {
                    dao.deleteBuilder().hardDelete().deleteByKeyList(deleteKeyValueList);
                }
            }
            List<Object> keyValueList = list.stream().map(p -> p.get(keyField))
                    .filter(p -> !ObjectUtils.isEmpty(p)).collect(Collectors.toList());
            List<Map<String, Object>> result = dao.selectBuilder().selectByKeyList(keyValueList);
            if (!CollectionUtils.isEmpty(result)) {
                keyValueList = result.stream().map(p -> p.get(keyField)).collect(Collectors.toList());
                dao.restoreBuilder().restoreByKeyList(keyValueList);
                return keyValueList;
            }
            return Collections.emptyList();
        } finally {
            if (!bl) {
                DaoConfigContextHolder.clearIncludeDisable();
            }
        }
    }

    private void setFixedFieldValue(List<Map<String, Object>> list) {
        Map<String, Object> fixMap = getFixedFieldValue();
        for (String key : uniqueKey) {
            Object val = fixMap.get(key);
            if (ObjectUtils.isEmpty(val)) {
                continue;
            }
            for (Map<String, Object> t : list) {
                Object obj = t.get(key);
                // 如果值为空可能是固定字段
                if (ObjectUtils.isEmpty(obj)) {
                    t.put(key, val);
                }
            }
        }
    }
    
    private void setCreateAndUpdateUser(List<Map<String, Object>> list) {
        // 设置创建人、更新人（用于唯一键包含字段判断）
        String createUserFieldName = daoConfig.getCreateUser();
        String updateUserFieldName = daoConfig.getUpdateUser();
        if (!StringUtils.hasLength(createUserFieldName) && !StringUtils.hasLength(updateUserFieldName)) {
            return;
        }
        Object userId = getCurrentUser();
        for (Map<String, Object> map : list) {
            if (StringUtils.hasLength(createUserFieldName)) {
                Object obj = map.get(createUserFieldName);
                if (ObjectUtils.isEmpty(obj)) {
                    map.put(createUserFieldName, userId);
                }
            }
            if (StringUtils.hasLength(updateUserFieldName)) {
                Object obj = map.get(updateUserFieldName);
                if (ObjectUtils.isEmpty(obj)) {
                    map.put(updateUserFieldName, userId);
                }
            }
        }
    }

    private List<Map<String, Object>> listDataByUniqueKey(List<Map<String, Object>> list) {
        if (ObjectUtils.isEmpty(uniqueKey)) {
            return Collections.emptyList();
        }
        List<Map<String, Object>> result;
        if (uniqueKey.length > 1) {
            List<LinkedHashMap<String, Object>> paramList = new ArrayList<>();
            for (Map<String, Object> t : list) {
                LinkedHashMap<String, Object> map = new LinkedHashMap<>();
                for (String key : uniqueKey) {
                    Object obj = t.get(key);
                    map.put(key, obj);
                }
                paramList.add(map);
            }
            result = dao.selectBuilder().multiField().select(paramList);
        } else {
            String key = uniqueKey[0];
            List<Object> fieldValueList = new ArrayList<>();
            for (Map<String, Object> t : list) {
                fieldValueList.add(t.get(key));
            }
            result = dao.selectBuilder().field(key).select(fieldValueList);
        }
        return result;
    }
}
